#ifndef TB_REFLECTION_reflection_PROPERTY_HPP__
#define TB_REFLECTION_reflection_PROPERTY_HPP__

#include "reflection_export.hpp"

TB_NAMESPACE_BEGIN

namespace reflection {

enum PropertyFlag {
  FLAG_NONE = 0,
  FLAG_READ = 1 << 0,
  FLAG_WRITE = 1 << 1,
  FLAG_READ_WRITE = FLAG_READ | FLAG_WRITE,
};

enum PropertyType {
  TYPE_NONE       = 0,
  TYPE_FIELD      = 1 << 0,
  TYPE_PROP       = 1 << 1,
  TYPE_FIELD_PROP = TYPE_FIELD | TYPE_PROP,
};

struct Property {
  Property(uint32 typeId_, uint32 classId_, const TCHAR* name_, const TCHAR* propName_, PropertyType pt = TYPE_NONE)
    : typeId(typeId_), classId(classId_), name(name_), propName(propName_), type(pt), flag(FLAG_READ_WRITE) {}
  
  virtual void Set(const void* obj, const Variant& val, bool raw = false) const {}
  virtual Variant Get(const void* obj, bool raw = false) const { return Variant(); }
  Property& SetFlag(PropertyFlag f) { flag = f; return *this; }

  String name;
  String propName;
  uint32 classId;
  uint32 typeId;
  PropertyFlag flag;
  PropertyType type;
};

template <typename ClassType, typename FieldHolder, typename ValueType>
struct PropertyField : public Property {
	typedef typename remove_crv<ClassType>::type class_type;

	typedef typename remove_crv<ValueType>::type value_type;
  
  PropertyField(FieldHolder fieldHoder, uint32 classId, const TCHAR* name, const TCHAR* propName)
      :Property(GetVariantTypeId<ValueType>(), classId, name, propName, TYPE_FIELD), field_(fieldHoder) {}

  virtual void Set(const void* obj, const Variant& val, bool raw = false) const {
    (static_cast<class_type*>(const_cast<void*>(obj)))->*field_ = val.Value<value_type>();
  }

  virtual Variant Get(const void* obj, bool raw = false) const {
    return Variant::FromValue((static_cast<class_type*>(const_cast<void*>(obj)))->*field_);
  }

 protected:
  FieldHolder field_;
};

template <typename ClassType, typename GetterHolder, typename SetterHolder, typename ValueType>
struct PropertyProp : public Property {
	typedef typename remove_crv<ClassType>::type class_type;

	typedef typename remove_crv<ValueType>::type value_type;
  
  PropertyProp(GetterHolder fgh, SetterHolder fsh, uint32 classId, const TCHAR* name, const TCHAR* propName)
    :Property(GetVariantTypeId<ValueType>(), classId, name, propName, TYPE_PROP), getter_(fgh), setter_(fsh) {}

  virtual void Set(const void* obj, const Variant& val, bool raw = false) const {
    ((static_cast<class_type*>(const_cast<void*>(obj)))->*setter_)(val.Value<value_type>());
  }

  virtual Variant Get(const void* obj, bool raw = false) const {
    return Variant::FromValue(((static_cast<class_type*>(const_cast<void*>(obj)))->*getter_)());
  }
 protected:
  GetterHolder getter_;
  SetterHolder setter_;
};

template <typename ClassType, typename DataType, typename DataHolder, typename FieldHolder, typename GetterHolder, typename SetterHolder, typename ValueType>
struct PropertyFieldAndProp : public Property {
  typedef typename remove_crv<ClassType>::type class_type;

	typedef typename remove_crv<ValueType>::type value_type;
  
  PropertyFieldAndProp(DataHolder dh, FieldHolder fh, GetterHolder fgh, SetterHolder fsh, uint32 classId, const TCHAR* name, const TCHAR* propName)
    :Property(GetVariantTypeId<ValueType>(), classId, name, propName, TYPE_FIELD_PROP), data_(dh), field_(fh), getter_(fgh), setter_(fsh) {}
  
  virtual void Set(const void* obj, const Variant& val, bool raw = false) const {
    if (raw) {
      static_cast<DataType*>(&(static_cast<class_type*>(const_cast<void*>(obj))->*data_))->*field_ = val.Value<value_type>();
    } else {
      ((static_cast<class_type*>(const_cast<void*>(obj)))->*setter_)(val.Value<value_type>());
    }
  }

  virtual Variant Get(const void* obj, bool raw = false) const {
    if (raw) {
      return Variant::FromValue(static_cast<DataType*>(&(static_cast<class_type*>(const_cast<void*>(obj))->*data_))->*field_);
    } else {
      return Variant::FromValue(((static_cast<class_type*>(const_cast<void*>(obj)))->*getter_)());
    }
  }

 protected:
  DataHolder data_;
  FieldHolder field_;
  GetterHolder getter_;
  SetterHolder setter_;
};

template <typename ClassType, typename FieldHolder, typename ValueType, typename T, uint32 N>
struct UnaryArrayField : public Property {
	typedef typename remove_crv<ClassType>::type class_type;

	typedef typename remove_crv<ValueType>::type value_type;

	typedef typename remove_crv<T>::type element_type;

	UnaryArrayField(FieldHolder fieldHoder, uint32 classId, const TCHAR* name, const TCHAR* propName)
		:Property(GetVariantTypeId<ValueType>(), classId, name, propName, TYPE_FIELD), field_(fieldHoder) {}

	virtual void Set(const void* obj, const Variant& val, bool raw = false) const {
		(void)raw;
		element_type* arr = static_cast<class_type*>(const_cast<void*>(obj))->*field_;
		TB_ASSERT(val.IsArray() && val.Size() >= N);
		Variant** v = val.GetArray();
		for (uint32 i = 0; i < N; ++i) {
			*(arr + i) = (*(v + i))->Value<element_type>();
		}
	}

	virtual Variant Get(const void* obj, bool raw = false) const {
		(void)raw;
		const element_type* arr = static_cast<class_type*>(const_cast<void*>(obj))->*field_;
		Variant v(0, Variant::kComplexTypeFlag, Variant::kArrayDataFlag);
		for (uint32 i = 0; i < N; ++i) {
			v.PushBack(Variant::FromValue(*(arr + i)));
		}
		return v;
	}

protected:
	FieldHolder field_;
};

//unary array
template <typename ClassType, typename FieldHolder, typename ValueType>
struct MakeArrayFieldDispatcher {
	static Property* Make(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
		BOOST_STATIC_ASSERT(!is_void<typename ArrayTrait<ValueType>::element_type>::value && ArrayTrait<ValueType>::size > 0);
		return new UnaryArrayField<ClassType, FieldHolder, ValueType, typename ArrayTrait<ValueType>::element_type, ArrayTrait<ValueType>::size>(field, classId, name, propName);
	}
};

template <typename ClassType, typename FieldHolder, typename ValueType>
Property* MakeArrayFieldImpl(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
	return MakeArrayFieldDispatcher<typename ClassType, typename FieldHolder, typename ValueType>::Make(field, classId, name, propName);
}

template <typename ClassType, typename FieldHolder, typename ValueType, typename IsRawArray = void>
struct MakePropertyFieldDispatcher {
	static Property* Make(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
		return new PropertyField<ClassType, FieldHolder, ValueType>(field, classId, name, propName);
	}
};

template <typename ClassType, typename FieldHolder, typename ValueType> struct 
MakePropertyFieldDispatcher<ClassType, FieldHolder, ValueType, typename enable_if<is_array<ValueType>::value >::type > {
	static Property* Make(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
	return MakeArrayFieldImpl<ClassType, FieldHolder, ValueType>(field, classId, name, propName);
	}
};

template <typename ClassType, typename FieldHolder, typename ValueType>
Property* MakePropertyFieldImpl(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
	return MakePropertyFieldDispatcher<ClassType, FieldHolder, ValueType>::Make(field, classId, name, propName);
}

template <typename ClassType, typename FieldHolder>  
Property* MakePropertyField(FieldHolder field, uint32 classId, const TCHAR* name, const TCHAR* propName) {
	return MakePropertyFieldImpl<ClassType, FieldHolder, boost::remove_reference<boost::function_types::result_type<FieldHolder>::type>::type>(field, classId, name, propName);
};

template <typename ClassType, typename GetterHolder, typename SetterHolder>
Property* MakePropertyProp(GetterHolder get, SetterHolder set, uint32 classId, const TCHAR* name, const TCHAR* propName) {
  return new PropertyProp<ClassType, GetterHolder, SetterHolder, boost::function_types::result_type<GetterHolder>::type>(get, set, classId, name, propName);
}

template <typename ClassType, typename DataType, typename DataHolder, typename FieldHolder, typename GetterHolder, typename SetterHolder>
Property* MakePropertyFieldAndProp(DataHolder data, FieldHolder field, GetterHolder get, SetterHolder set, uint32 classId, const TCHAR* name, const TCHAR* propName) {
  return new PropertyFieldAndProp<ClassType, DataType, DataHolder, FieldHolder, GetterHolder, SetterHolder, boost::function_types::result_type<GetterHolder>::type>(data, field, get, set, classId, name, propName);
}

} //namespace reflection

TB_NAMESPACE_END

#endif //TB_REFLECTION_reflection_PROPERTY_HPP__
